package com.systop.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.systop.constant.AuthServiceConstant;
import com.systop.constant.OrderConstant;
import com.systop.domain.to.OrderTo;
import com.systop.domain.to.SucLoginUserTo;
import com.systop.domain.vo.CartItem;
import com.systop.domain.vo.CartVo;
import com.systop.domain.vo.GoodsSalesVo;
import com.systop.domain.vo.OrderConfirmVo;
import com.systop.domain.vo.OrderSubmitResponseVo;
import com.systop.domain.vo.OrderSubmitVo;
import com.systop.domain.vo.SumSalesVo;
import com.systop.entity.Consignee;
import com.systop.entity.Goods;
import com.systop.entity.GoodsStock;
import com.systop.entity.Order;
import com.systop.entity.SysUser;
import com.systop.dao.OrderDao;
import com.systop.exception.NoStockException;
import com.systop.interceptor.LoginInterceptor;
import com.systop.service.ConsigneeService;
import com.systop.service.GoodsService;
import com.systop.service.GoodsStockService;
import com.systop.service.OrderService;
import com.systop.service.ShopCartService;
import com.systop.service.SysUserService;
import com.systop.utils.BizCodeEnum;
import com.systop.utils.RedisUtil;
import io.swagger.v3.oas.annotations.security.OAuthFlow;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

@Service
public class OrderServiceImpl extends ServiceImpl<OrderDao,Order> implements OrderService {

    private ThreadLocal<OrderSubmitVo> confirmVoThreadLocal = new ThreadLocal<>();

    @Autowired
    private OrderDao orderDao;

    @Autowired
    ConsigneeService consigneeService;

    @Autowired
    SysUserService sysUserService;

    @Autowired
    ShopCartService shopCartService;

    @Autowired
    GoodsService goodsService;

    @Autowired
    GoodsStockService goodsStockService;

    @Autowired
    RedisUtil redisUtil;

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    AmqpTemplate amqpTemplate;

    @Autowired
    ThreadPoolExecutor executor;

    //查询订单数据
    @Override
    public List<Order> findAllOrders(Order order) {
        return orderDao.findAllOrders(order);
    }
    //查询符合条件的订单总数
    @Override
    public int countOrders(Order order) {
        return orderDao.countOrders(order);
    }
    //查询所有用户名,返回到下拉框
    public List<SysUser> findAllUser(){
        return orderDao.findAllUser();
    };
    //查询所有收货人，前台根据相关用户名显示对应收货人
    public List<Consignee> findAllConsignee(){
        return orderDao.findAllConsignee();
    };
    //查询所有商品
    public List<Goods> findAllGoods(){
        return orderDao.findAllGoods();
    };

    //根据商品id查询单价和规格
    public Goods findPriceById(Order order){
        return orderDao.findPriceById(order);
    };
    //添加订单
    public int addOrder(Order order){
        return orderDao.addOrder(order);
    };

    //删除订单
    public int delOrder(Order order){
        return orderDao.delOrder(order);
    }

    private List<Order> queryMyOrderList(Long userId){
        return orderDao.queryMyOrderList(userId);
    }

    @Override
    public List<OrderTo> queryMyOrderList(SysUser user) throws ExecutionException, InterruptedException {
        //查询所有当前用户订单
        CompletableFuture<List<Order>> orderSync = CompletableFuture.supplyAsync(() -> {
            // return this.list(new QueryWrapper<Order>().
            //         eq("user_id", user.getUserId()));
            List<Order> orders = this.queryMyOrderList(user.getUserId());
            return orders;
        });
        //查询所有当前用户收货地址
        CompletableFuture<List<Consignee>> consigneeSync = CompletableFuture.supplyAsync(() -> {
            return consigneeService.list(new QueryWrapper<Consignee>()
                    .eq("user_id", user.getUserId()));
        });

        CompletableFuture.allOf(orderSync,consigneeSync).thenRunAsync(
                ()->{
                    System.out.println("查询完毕");
                }
        ).get();

        //
        Collection<List<Order>> orderList = orderSync.get().stream().collect(
                Collectors.groupingBy(Order::getOrderNo, Collectors.toList())).values();

        List<Consignee> consigneeList = consigneeSync.get();

        List<OrderTo> orderToList = orderList.stream().map(
                orderItem -> {
                    //准备阶段
                    OrderTo orderTo = new OrderTo();
                    //填充订单列表
                    orderTo.setGoodsList(orderItem);

                    //填充收货地址信息
                    Order order = orderItem.get(0);
                    for (Consignee consignee : consigneeList) {
                        if (consignee.getConsigneeId() == order.getConsigneeId()){
                            orderTo.setConsignee(consignee);
                        }
                    }

                    //判断所有订单状态是否相等
                    Long count = orderItem.stream().map(Order::getOrderStatus).distinct().count();

                    if (count == 1) {
                        orderTo.setOrderStatus(order.getOrderStatus());
                    }

                    //返回单号
                    orderTo.setOrderNo(order.getOrderNo());

                    //填充订单总价，订单商品总数
                    //商品总数
                    Integer goodsNum = 0;
                    //商品总价
                    Double goodsSum = 0d;
                    for (Order item : orderItem){
                        goodsNum += item.getGoodsNum();
                        goodsSum += item.getSumPrice();
                    }
                    orderTo.setGoodsNum(goodsNum);
                    orderTo.setGoodsSum(new BigDecimal(goodsSum));

                    return orderTo;
                }
        ).sorted().collect(Collectors.toList());

        return orderToList;
    }

    @Override
    public OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException {
        //获取当前登录用户信息
        SucLoginUserTo sucLoginUserTo = LoginInterceptor.threadLocal.get();
        SysUser user = sysUserService.findUserByUserCode(sucLoginUserTo.getUserCode());
        //设置redisKey
        String cartKey = AuthServiceConstant.CART_PREFIX + user.getUserId();

        OrderConfirmVo confirmVo = new OrderConfirmVo();

        //查询所有当前用户收货地址
        CompletableFuture<Void> getConsignee = CompletableFuture.runAsync(() -> {
            List<Consignee> consignees = consigneeService.list(new QueryWrapper<Consignee>()
                    .eq("user_id", user.getUserId()));
            confirmVo.setAddress(consignees);
        },executor);

        //查询所有购物车商品的信息
        CompletableFuture<Void> getCartItems = CompletableFuture.runAsync(() -> {
            //获取所有商品
            List<CartItem> cartList =
                    redisUtil.hmget(cartKey).values().
                            stream().map(item->(CartItem)item).sorted().collect(Collectors.toList());

            if (cartList != null && cartList.size() > 0) {
                CartVo cartVo = new CartVo();
                List<CartItem> collect = cartList.stream().filter(CartItem::getIsCheck)
                        .filter(cartItem -> {
                            GoodsStock goodsStock = goodsStockService.getOne(new QueryWrapper<GoodsStock>().eq("goods_id", cartItem.getGoodsId()));
                            //总剩余库存量
                            cartItem.setStockNum(goodsStock.getStockNum() - goodsStock.getLockStockNum());
                            if((goodsStock.getStockNum() - goodsStock.getLockStockNum()) <= 0){
                                //如果库存本身不足则过滤当前商品
                                return false;
                            }else if((goodsStock.getStockNum() - goodsStock.getLockStockNum()) - cartItem.getCount() <= 0){
                                //如果库存量不够则显示最大能购买的商品量
                                cartItem.setCount(goodsStock.getStockNum() - goodsStock.getLockStockNum());
                            }
                            return true;
                        })
                        .map(item -> {
                            //更新价格
                            item.setPrice(goodsService.queryPrice(item.getGoodsId()));
                            return item;
                        })
                        .collect(Collectors.toList());
                cartVo.setCartItem(collect);
                confirmVo.setCartVo(cartVo);
            }
        },executor);

        CompletableFuture.allOf(getConsignee,getCartItems).get();

        //设置订单令牌
        String token = RandomStringUtils.randomAlphanumeric(15);
        //redis存储订单令牌
        redisUtil.set(OrderConstant.USER_ORDER_TOKEN_PREFIX+user.getUserId(), token);
        confirmVo.setOrderToken(token);
        return confirmVo;
    }

    @Transactional
    @Override
    public OrderSubmitResponseVo submitOrder(OrderSubmitVo orderSubmitVo) {
        OrderSubmitResponseVo responseVo = new OrderSubmitResponseVo();

        //获取当前登录用户信息
        SucLoginUserTo sucLoginUserTo = LoginInterceptor.threadLocal.get();
        SysUser user = sysUserService.findUserByUserCode(sucLoginUserTo.getUserCode());
        //设置redisKey
        String cartKey = AuthServiceConstant.CART_PREFIX + user.getUserId();

        //1、验证令牌
        /* 0【令牌失败】 - 1【删除成功】 */
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        String orderToken = orderSubmitVo.getToken();

        //原子验证令牌和删除令牌
        Long result = (Long) redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class),
                Arrays.asList(OrderConstant.USER_ORDER_TOKEN_PREFIX + user.getUserId()), orderToken);
        if (result == 0L) {
            //删除失败
            responseVo.setCode(BizCodeEnum.TOKEN_VERIFY.getCode());
            return responseVo;
        }

        /**
         * 创建订单
         */
        //1、生成一个订单号
        String orderSn = IdWorker.getTimeId();

        //2、构建所有订单项
        List<Order> orderList = redisUtil.hmget(cartKey).values().
                stream().map(item -> (CartItem) item).filter(CartItem::getIsCheck).map(item -> {
            Order order = new Order();
            order.setUserId(user.getUserId());
            order.setOrderNo(orderSn);
            order.setOrderStatus(0);
            order.setConsigneeId(orderSubmitVo.getConsigneeId());
            order.setGoods(goodsService.getById(item.getGoodsId()));
            order.setGoodsId(item.getGoodsId());
            //库存对比
            GoodsStock goodsStock = goodsStockService.getStockByGoodsId(item.getGoodsId());

            //总剩余库存量
            if((goodsStock.getStockNum() - goodsStock.getLockStockNum()) - item.getCount() <= 0){
                //如果库存量不够则显示最大能购买的商品量
                order.setGoodsNum(goodsStock.getStockNum() - goodsStock.getLockStockNum());
            }else{
                order.setGoodsNum(item.getCount());
            }
            order.setSumPrice(order.getGoods().getPrice() * order.getGoodsNum());
            return order;
        }).collect(Collectors.toList());

        //3、验价
        /*
            计算总价
         */
        BigDecimal sumPrice = new BigDecimal(orderList.stream().map(Order::getSumPrice).reduce(Double::sum).get().toString());
        BigDecimal payPrice = orderSubmitVo.getPayPrice();
        if (Math.abs(sumPrice.subtract(payPrice).doubleValue()) >= 0.01) {
            //金额对比失败
            responseVo.setCode(BizCodeEnum.AMOUNT_COMPARISON_FAILED.getCode());
            return responseVo;
        }

        //4、保存订单
        saveOrder(orderList);
        for (Order order : orderList) {
            amqpTemplate.convertAndSend("order-event-exchange","order.create.order",order);
        }

        //5、锁定库存
        Boolean lockedStock = goodsStockService.orderLockStock(orderList);
        if (!lockedStock) {
            // responseVo.setCode(BizCodeEnum.STOCK_LOCK_FAIL.getCode());
            throw new NoStockException(BizCodeEnum.STOCK_LOCK_FAIL.getMsg());
        }
        responseVo.setOrderList(orderList);
        return responseVo;
    }

    /**
     * 关闭订单
     * @param orderList
     */
    @Override
    public void closeOrder(List<Order> orderList) {
        if(orderList!=null){
            long count = orderList.stream().filter(item -> {
                if (item.getOrderStatus() == 0) {
                    return true;
                }
                return false;
            }).count();
            if (orderList.size() == count) {
                this.update(new UpdateWrapper<Order>()
                        .eq("order_no", orderList.get(0).getOrderNo())
                        .set("order_status", 2));
            }
            try{
                //通过日志记录所有发送的消息(可以通过一张日志记录表记录)
                amqpTemplate.convertAndSend("order-event-exchange","order.release.other",orderList.get(0));
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }

    /**
     * 保存订单项信息
     * @param orderList
     */
    private void saveOrder(List<Order> orderList) {
        List<Order> addOrderList = orderList.stream().map(orderItem -> {
            System.out.println(new Date());
            orderItem.setModifyTime(new Date());
            orderItem.setCreateTime(new Date());
            return orderItem;
        }).collect(Collectors.toList());

        this.saveBatch(addOrderList);
    }

    @Override
    public List<SumSalesVo> querySalesGroupByMonth(String year,String month) {
        //查询redis中是否存在相应数据(只查询按照年份查询的结果)
        Object hget = null;
        if(month == null || month.equals(""))
            hget = redisUtil.hget("sum:sale", year);
        //不存在数据直接到数据库中查询返回
        if(hget==null){
            List<SumSalesVo> queryList = orderDao.querySalesGroupByMonth(year,month);
            //存储查询数据,过期时间为1周(只存储按照年份查询的结果)
            if(queryList!=null && queryList.size()>0 && (month == null || month.equals("")))
                redisUtil.hset("sum:sale",year,JSON.toJSONString(queryList),604800);
            return queryList;
        }
        //存在数据直接返回redis数据
        List<SumSalesVo> redisList =
                JSON.parseArray(hget.toString(), SumSalesVo.class);
        return redisList;
    }

    @Override
    public List<GoodsSalesVo> querySalesDetail(String year, String month) {
        //查询redis中是否存在相应数据(只查询按照年份查询的结果)
        Object hget = redisUtil.hget("sales:details:"+year, month);
        if(hget == null){
            List<GoodsSalesVo> salesDetailList = orderDao.querySalesDetail(year,month);
            //存储查询数据,过期时间为1周
            if(salesDetailList!=null && salesDetailList.size()>0)
                redisUtil.hset("sales:details:"+year, month, JSON.toJSONString(salesDetailList),604800);
            return salesDetailList;
        }
        //存在数据直接返回redis数据
        List<GoodsSalesVo> redisList =
                JSON.parseArray(hget.toString(), GoodsSalesVo.class);
        return redisList;
    }
}
